package com.kmxd.ams.app.search;

import static com.kmxd.ams.core.common.constant.Constants.SEARCH_SVC_PREFIX;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.kmxd.ams.client.archive.IArchiveTypeSvc;
import com.kmxd.ams.client.archive.dto.ArchiveTypeQueryDTO;
import com.kmxd.ams.client.archive.vo.ArchiveTypeListVO;
import com.kmxd.ams.client.arrange.IBoxSvc;
import com.kmxd.ams.client.arrange.IDocumentSvc;
import com.kmxd.ams.client.arrange.IProjectSvc;
import com.kmxd.ams.client.arrange.IVolumeSvc;
import com.kmxd.ams.client.search.ISearchSvc;
import com.kmxd.ams.client.search.dto.SearchByConditionDTO;
import com.kmxd.ams.client.search.dto.SearchByKeywordsDTO;
import com.kmxd.ams.core.common.constant.ArchConst;
import com.kmxd.ams.core.common.constant.Constants;
import com.kmxd.ams.core.common.constant.ErrorCode;
import com.kmxd.ams.core.exception.BizTipException;
import com.kmxd.ams.core.util.*;
import com.kmxd.ams.infra.arrange.entity.Box;
import com.kmxd.ams.infra.arrange.entity.Document;
import com.kmxd.ams.infra.arrange.entity.Project;
import com.kmxd.ams.infra.arrange.entity.Volume;
import com.kmxd.ams.infra.tmpl.dto.ArchTmplBO;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

/**
 * 检索mysql数据
 *
 * @author nickbi
 */
@Service(SEARCH_SVC_PREFIX + SearchTypeConst.MYSQL)
@AllArgsConstructor
public class SearchMysqlSvc implements ISearchSvc {

  private final IDocumentSvc documentSvc;
  private final IVolumeSvc volumeSvc;
  private final IBoxSvc boxSvc;
  private final IProjectSvc projectSvc;
  private final IArchiveTypeSvc archiveTypeSvc;
  /**
   * 数据检索工厂方法，
   *
   * @param req 请求参数
   * @return 校验结果
   */
  @Override
  public Page archByCondition(SearchByConditionDTO req) {
    String tableId = req.getTableId();
    if (tableId.contains(ArchTmplBO.DOCUMENT)) {
      return docByCondition(req);
    } else if (tableId.contains(ArchTmplBO.VOLUME)) {
      return volByCondition(req);
    } else if (tableId.contains(ArchTmplBO.BOX)) {
      return boxByCondition(req);
    } else if (tableId.contains(ArchTmplBO.PROJECT)) {
      return projByCondition(req);
    }
    throw BizTipException.instance(ErrorCode.NOT_FOUND, "未知档案类型");
  }

  public Page<Document> docByCondition(SearchByConditionDTO req) {
    QueryWrapper<Document> wrapper = Wrappers.query();
    buildQuery(req, wrapper);
    if (BooleanUtil.isTrue(req.getOutArch())){
      wrapper.gt(FieldUtil.getColumn(Document::getStorageNum),0);
    }
    return documentSvc.page(new Page<>(req.getCurrent(), req.getSize()), wrapper);
  }

  public Page<Volume> volByCondition(SearchByConditionDTO req) {
    QueryWrapper<Volume> wrapper = Wrappers.query();
    buildQuery(req, wrapper);
    if (BooleanUtil.isTrue(req.getOutArch())){
      wrapper.gt(FieldUtil.getColumn(Volume::getStorageNum),0);
    }
    return volumeSvc.page(new Page<>(req.getCurrent(), req.getSize()), wrapper);
  }

  public Page<Box> boxByCondition(SearchByConditionDTO req) {
    QueryWrapper<Box> wrapper = Wrappers.query();
    buildQuery(req, wrapper);
    if (BooleanUtil.isTrue(req.getOutArch())){
      wrapper.gt(FieldUtil.getColumn(Box::getStorageNum),0);
    }
    return boxSvc.page(new Page<>(req.getCurrent(), req.getSize()), wrapper);
  }

  public Page<Project> projByCondition(SearchByConditionDTO req) {
    QueryWrapper<Project> wrapper = Wrappers.query();
    buildQuery(req, wrapper);
    return projectSvc.page(new Page<>(req.getCurrent(), req.getSize()), wrapper);
  }

  /**
   * 创建查询
   *
   * @param req
   * @param wrapper
   * @param <T>
   */
  private <T> void buildQuery(SearchByConditionDTO req, QueryWrapper<T> wrapper) {
    String sql = QueryUtil.buildSql(req.getCondition(), null);
    DynamicTableNameUtil.setTableName(req.getTableId());
    wrapper.eq(ArchConst.DEF_STATUS, req.getStatus());
    if (StrUtil.isNotBlank(sql)) {
      wrapper.last(" and " + sql);
    }
  }

  @Override
  public List<Tree<String>> selectArchTree(SearchByKeywordsDTO req) {
    // 查询当前全宗下档案库
    ArchiveTypeQueryDTO queryDTO = new ArchiveTypeQueryDTO();
    queryDTO.setFondsId(SecurityUtil.getFondsId());
    List<ArchiveTypeListVO> list = archiveTypeSvc.selectList(queryDTO);
    List finalTreeNodeList = Lists.newArrayList();
    List treeNodeList =
        list.parallelStream()
            .map(
                item -> {
                  if (ObjectUtil.isNotEmpty(item.getDocTableId())) {
                    Document qryDoc = ConvertUtil.toBean(req, Document.class);
                    qryDoc.setArchTypeId(item.getId());
                    qryDoc.setTableId(item.getDocTableId());
                    long docCount = documentSvc.count(qryDoc);
                    if (0 != docCount) {
                      TreeNode docNode =
                          buildTreeNode(
                              req.getKeywords(),
                              item.getSort(),
                              item.getDocTableId(),
                              "文件",
                              item.getId().toString(),
                              docCount);
                      finalTreeNodeList.add(docNode);
                    }
                  }
                  if (item.getHaveBox()) {
                    Box qryBox = ConvertUtil.toBean(req, Box.class);
                    qryBox.setArchTypeId(item.getId());
                    qryBox.setTableId(item.getBoxTableId());
                    long boxCount = boxSvc.count(qryBox);
                    if (0 != boxCount) {
                      TreeNode boxNode =
                          buildTreeNode(
                              req.getKeywords(),
                              item.getSort(),
                              item.getBoxTableId(),
                              "盒",
                              item.getId().toString(),
                              boxCount);
                      finalTreeNodeList.add(boxNode);
                    }
                  }
                  if (item.getHaveVol()) {
                    Volume qryVol = ConvertUtil.toBean(req, Volume.class);
                    qryVol.setArchTypeId(item.getId());
                    qryVol.setTableId(item.getVolTableId());
                    long volCount = volumeSvc.count(qryVol);
                    if (0 != volCount) {
                      TreeNode volNode =
                          buildTreeNode(
                              req.getKeywords(),
                              item.getSort(),
                              item.getVolTableId(),
                              "案卷",
                              item.getId().toString(),
                              volCount);
                      finalTreeNodeList.add(volNode);
                    }
                  }
                  if (item.getHaveProject()) {
                    Project qryProj = ConvertUtil.toBean(req, Project.class);
                    qryProj.setArchTypeId(item.getId());
                    qryProj.setTableId(item.getProjTableId());
                    long projCount = projectSvc.count(qryProj);
                    if (0 != projCount) {
                      TreeNode projNode =
                          buildTreeNode(
                              req.getKeywords(),
                              item.getSort(),
                              item.getProjTableId(),
                              "项目",
                              item.getId().toString(),
                              projCount);
                      finalTreeNodeList.add(projNode);
                    }
                  }
                  return buildTreeNode(
                      req.getKeywords(),
                      item.getSort(),
                      item.getId().toString(),
                      item.getName(),
                      Constants.DEFAULT_TOP_TREE_PID,
                      null);
                })
            .collect(Collectors.toList());
    treeNodeList.addAll(finalTreeNodeList);
    List<Tree<String>> treeList =
        TreeUtil.build(treeNodeList, StrUtil.toString(Constants.DEFAULT_TOP_TREE_PID));
    List<Tree<String>> tree =
        treeList.stream()
            .filter(item -> Objects.nonNull(item.getChildren()))
            .filter(item -> !item.getChildren().isEmpty())
            .collect(Collectors.toList());
    return CollectionUtils.isEmpty(tree) ? new ArrayList() : tree;
  }

  @NotNull
  private TreeNode buildTreeNode(
      String keywords, Integer sort, String tableId, String name, String pid, Long count) {
    TreeNode docNode = new TreeNode();
    docNode.setId(tableId);
    docNode.setName(name);
    docNode.setParentId(pid);
    docNode.setWeight(sort);
    Map<String, String> map = Maps.newHashMap();
    map.put("dataCondition", tableId);
    map.put("type", "tableId");
    if (StrUtil.isNotBlank(keywords)) {
      map.put("keywords", keywords);
    }
    Long num = Objects.nonNull(count) ? count : 0L;
    map.put("num", String.valueOf(num));
    docNode.setExtra(map);
    return docNode;
  }
}
